package correctness;

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.List;

import model.LogicType;
import model.editormodel.EditorModel;
import model.editormodel.Input;
import model.editormodel.LocsiParser;
import model.editormodel.LogicEditorElement;
import model.editormodel.Output;
import model.simulationmodel.LogicValue;
import model.simulationmodel.SimulationModel;
import model.simulationmodel.logicelements.LogicSimulationElement;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import view.UtilityConstants;
import controller.commoncontroller.UtilityDialogs;

/**
 * Dieser Testklasse dient dazu eine Schaltung mit einer großen Anzahl an NOT
 * Elementen zu erzeugen. Dabei werden alle Elemente in einer Reihe geschalten.
 * Das berechnete Ergebnis wird mittels Modulo Rechnung kontrolliert
 * 
 * @author Team Locsi
 * 
 */
public class QueueCircuitNotTest {
	// maximales Ausmass eines Bauteils
	private final static int RASTER = 100;
	// maximale Anzahl an Elementen die erzeugt werden sollen
	private final static int MAXELEMENTS = 500;
	// Typ der verwendet werden soll. Dabei darf nur ein Typ verwendet werden
	// der genau einen Eingang und einen Ausgang besitzt
	private final static LogicType type = LogicType.NOT;
	// Name der Schaltung, der beim abspeichern verwendet wird
	String name = "queueCircuitOneToOneTest";

	// Objekte die benoetigt werden
	private static EditorModel eModel;
	private static SimulationModel sModel;
	private static LocsiParser locsiParser;
	private static LogicEditorElement setter;
	private static LogicEditorElement getter;
	private LogicValue justValue;
	private static LogicValue requiredValue;

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		eModel = new EditorModel();
		sModel = new SimulationModel(eModel);
	}

	@Before
	public void setUp() throws Exception {
		List<Input> inputs = new ArrayList<Input>();
		List<Output> outputs = new ArrayList<Output>();
		locsiParser = new LocsiParser(eModel);

		// hinzufügen der Elemente ins eModel
		for (int n = 0; n < MAXELEMENTS; ++n) {
			eModel.addLogicElement(type, null, n * RASTER + RASTER, RASTER);
		}

		// damit simuliert werden kann, benötigen wir auch einen Schalter und
		// eine Lampe
		eModel.addLogicElement(LogicType.SWITCH, null, 0, 0);
		eModel.addLogicElement(LogicType.LAMP, null, RASTER * MAXELEMENTS + 2
				* RASTER, RASTER);

		// Referenz auf Lampe und Schalter halten
		for (LogicEditorElement e : eModel.getCircuitModel()
				.getUnmodifableElementsOnEditor().values()) {
			if (e.getType() == LogicType.LAMP) {
				getter = e;
			} else {
				if (e.getType() == LogicType.SWITCH) {
					setter = e;
				} else {
					inputs.add(e.getInputs().get(Input.STD_A));
					outputs.add(e.getOutputs().get(Output.STD_O));
				}
			}
		}

		// setzen der Verbindungen zwischen den Elementen
		for (int i = 0; i < inputs.size() - 1; ++i) {
			eModel.setConnection(outputs.get(i), inputs.get(i + 1));
		}

		// Anfügen des Schalters
		eModel.setConnection(setter.getOutputs().get(Output.STD_O),
				inputs.get(0));

		// Anfügen der Lampe
		eModel.setConnection(outputs.get(outputs.size() - 1), getter
				.getInputs().get(Input.STD_A));
	}

	@Test
	public void test() {
		// Speicherort
		String location = "test" + UtilityConstants.SEPARATOR + "model"
				+ UtilityConstants.SEPARATOR + "editormodel"
				+ UtilityConstants.SEPARATOR + "test_saved_circuits"
				+ UtilityConstants.SEPARATOR + name
				+ UtilityDialogs.XML_EXTENSION;

		// Die Schaltung wird im Standardordner abgespeichert um auftretende
		// Bugs leichter finden zu können, da sich die Konstellation bei jedem
		// Aufruf ändert
		assertTrue(
				"Schaltung konnte nicht gespeichert werden",
				locsiParser.saveCircuit(name, location,
						eModel.getCircuitModel()));

		// Nach Konstruktione muss diese Schaltung die Eigenschaften einer
		// Komponente erfuellen
		assertTrue(
				"Die erzeugte Schaltung erfuellt nicht die Eigenschaften einer Komponente",
				eModel.getCircuitModel().isComponent());

		// Nach Konstruktion ist diese Schaltung kein Schaltwerk, oder die
		// maximale Anzahl von Elementen wurde überschritten
		assertFalse("Der Schaltungstyp wurde falsch berechnet", eModel
				.getCircuitModel().isLogicCircuit());

		// erzeugen der LogicSimulationElements
		sModel.updateSimulationElements();

		// Refernzen auf die relevanten LogicSimulationElements setzen
		LogicSimulationElement set = sModel.getSimulationElements().get(setter);
		LogicSimulationElement get = sModel.getSimulationElements().get(getter);

		// Setzen des logischen Wertes des Schalters
		set.setInputValue(Input.STD_A, LogicValue.L0);

		// Berechnen der logischen Werte
		set.calcLogicValue();
		sModel.calcElementsToCalc();

		// Bestimmen der benötigten LogicValues
		justValue = get.getLogicValueOfInput(Input.STD_A);

		int modResult = MAXELEMENTS % 2;

		// Da es sich um eine Kette aus Not Gattern handelt, kann man mittels
		// modulo Rechnung auf den logischen Wert schliessen
		if (modResult == 1) {
			requiredValue = LogicValue.L1;
		} else {
			requiredValue = LogicValue.L0;
		}
		assertTrue("Die Simulation war inkorrekt", justValue == requiredValue);

		// Wechseln des logischen Wertes am Kettenanfang
		// ---------------------------------------------------------------------
		// Setzen des logischen Wertes des Schalters
		set.setInputValue(Input.STD_A, LogicValue.L1);

		// Berechnen der logischen Werte
		set.calcLogicValue();
		sModel.calcElementsToCalc();

		// Bestimmen der benötigten LogicValues
		justValue = get.getLogicValueOfInput(Input.STD_A);

		// Da es sich um eine Kette aus Not Gattern handelt, kann man mittels
		// modulo Rechnung auf den logischen Wert schliessen
		if (modResult == 1) {
			requiredValue = LogicValue.L0;
		} else {
			requiredValue = LogicValue.L1;
		}
		assertTrue("Die Simulation war inkorrekt", justValue == requiredValue);
	}
}
